blob: 5c5df7f03aa146c94738a63c96257e8927fd01e4 [file] [log] [blame]
// 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 std::borrow::{Borrow, BorrowMut};
use std::cell::RefCell;
use std::rc::Rc;
use lazy_static::lazy_static;
use crate::skywalking::agent::reporter::Reporter;
use crate::skywalking::core::{Context, ContextListener, Extractable, Injectable, Span, TracingContext};
use crate::skywalking::core::span::TracingSpan;
/// Thread Local tracing context. Host the context and propagate it in each thread if needed.
thread_local!( static CTX: RefCell<CurrentTracingContext> = RefCell::new(CurrentTracingContext::new()));
/// Global reporter
/// Status: WIP
lazy_static! {
static ref SKYWALKING_REPORTER : Reporter = {
Reporter::new()
};
}
pub struct ContextManager {}
impl ContextManager {
/// Run a closure under an entry span observing.
/// Span is automatically created, started and ended around the closure f.
pub fn tracing_entry<F>(operation_name: &str, extractor: Option<&dyn Extractable>, f: F)
where F: FnOnce(&mut Box<dyn Span>) {
CTX.with(|context| {
let span;
{
// Borrow mut ref has to end in this specific scope, as the context is nested used in f<F>
let mut mut_context = context.borrow_mut();
let parent_span_id = mut_context.parent_span_id();
span = mut_context.create_entry_span(operation_name, parent_span_id, extractor);
}
match span {
None => {}
Some(mut s) => {
s.start();
f(s.borrow_mut());
s.end();
let is_first_span = s.span_id() == 0;
let mut mut_context = context.borrow_mut();
mut_context.finish_span(s);
if is_first_span {
mut_context.finish();
}
}
};
});
}
/// Run a closure under an exit span observing.
/// Span is automatically created, started and ended around the closure f.
pub fn tracing_exit<F>(operation_name: &str, peer: &str, injector: Option<&dyn Injectable>, f: F)
where F: FnOnce(&mut Box<dyn Span>) {
CTX.with(|context| {
let span;
{
// Borrow mut ref has to end in this specific scope, as the context is nested used in f<F>
let mut mut_context = context.borrow_mut();
let parent_span_id = mut_context.parent_span_id();
span = mut_context.create_exit_span(operation_name, parent_span_id, peer, injector);
}
match span {
None => {}
Some(mut s) => {
s.start();
f(s.borrow_mut());
s.end();
let is_first_span = s.span_id() == 0;
let mut mut_context = context.borrow_mut();
mut_context.finish_span(s);
if is_first_span {
mut_context.finish();
}
}
};
});
}
/// Run a closure under a local span observing.
/// Span is automatically created, started and ended around the closure f.
pub fn tracing_local<F>(operation_name: &str, f: F)
where F: FnOnce(&mut Box<dyn Span>) {
CTX.with(|context| {
let span;
{
// Borrow mut ref has to end in this specific scope, as the context is nested used in f<F>
let mut mut_context = context.borrow_mut();
let parent_span_id = mut_context.parent_span_id();
span = mut_context.create_local(operation_name, parent_span_id);
}
match span {
None => {}
Some(mut s) => {
s.start();
f(s.borrow_mut());
s.end();
let is_first_span = s.span_id() == 0;
let mut mut_context = context.borrow_mut();
mut_context.finish_span(s);
if is_first_span {
mut_context.finish();
}
}
};
});
}
}
struct CurrentTracingContext {
option: Option<Box<WorkingContext>>,
}
struct WorkingContext {
context: Box<TracingContext>,
span_stack: Vec<i32>,
}
impl CurrentTracingContext {
/// Create the tracing context in the thread local at the first time.
fn new() -> Self {
CurrentTracingContext {
option: match TracingContext::new(SKYWALKING_REPORTER.service_instance_id()) {
Some(tx) => {
Some(Box::new(WorkingContext {
context: Box::new(tx),
span_stack: Vec::new(),
}))
}
None => { None }
},
}
}
/// Delegate to the tracing core entry span creation method, if current context is valid.
fn create_entry_span(&mut self, operation_name: &str, parent_span_id: Option<i32>, extractor: Option<&dyn Extractable>) -> Option<Box<dyn Span>> {
match self.option.borrow_mut() {
None => { None }
Some(wx) => {
let span = wx.context.create_entry_span(operation_name, parent_span_id, extractor);
wx.span_stack.push(span.span_id());
Some(span)
}
}
}
/// Delegate to the tracing core exit span creation method, if current context is valid.
fn create_exit_span(&mut self, operation_name: &str, parent_span_id: Option<i32>, peer: &str, injector: Option<&dyn Injectable>) -> Option<Box<dyn Span>> {
match self.option.borrow_mut() {
None => { None }
Some(wx) => {
let span = wx.context.create_exit_span(operation_name, parent_span_id, peer, injector);
wx.span_stack.push(span.span_id());
Some(span)
}
}
}
/// Delegate to the tracing core local span creation method, if current context is valid.
fn create_local(&mut self, operation_name: &str, parent_span_id: Option<i32>) -> Option<Box<dyn Span>> {
match self.option.borrow_mut() {
None => { None }
Some(wx) => {
let span = wx.context.create_local_span(operation_name, parent_span_id);
wx.span_stack.push(span.span_id());
Some(span)
}
}
}
/// Delegate to the tracing core span finish method, if current context is valid.
fn finish_span(&mut self, span: Box<dyn Span>) {
match self.option.borrow_mut() {
None => {}
Some(wx) => {
wx.context.finish_span(span);
wx.span_stack.pop();
}
};
}
/// Fetch the parent span id, to be used in next new span.
/// The span id(s) are saved in the span_stack by following as same the stack-style as span creation sequence.
fn parent_span_id(&self) -> Option<i32> {
match self.option.borrow() {
None => { None }
Some(wx) => {
match wx.span_stack.last() {
None => { None }
Some(span_id) => { Some(span_id.clone()) }
}
}
}
}
/// Finish the current tracing context, including
/// 1. Clear up the context
/// 2. Transfer the context to profobuf format and pass to reporter.
fn finish(&mut self) {
match self.option.borrow_mut() {
None => {}
Some(wx) => {
let tracingContext = &wx.context;
wx.span_stack.clear();
// TODO: Transfer tracingContext to protobuf
}
}
self.option = None;
}
}
#[cfg(test)]
mod context_tests {
use crate::skywalking::agent::context_manager::*;
use crate::skywalking::core::{ContextListener, Tag, TracingContext};
#[test]
fn test_context_manager() {
ContextManager::tracing_entry("op1", None, |mut span| {
span.tag(Tag::new(String::from("tag1"), String::from("value1")));
ContextManager::tracing_exit("op2", "127.0.0.1:8080", None, |mut span| {
span.set_component_id(33);
});
ContextManager::tracing_local("op3", |mut span| {});
});
}
struct MockReporter {}
impl ContextListener for MockReporter {
fn service_instance_id(&self) -> Option<i32> {
Some(1)
}
fn report_trace(&self, finished_context: Box<TracingContext>) {}
}
}